/* mostly come from riscv-spi document */
#ifndef _SBI_H
#define _SBI_H

#define SBI_SET_TIMER 0
#define SBI_CONSOLE_PUTCHAR 1
#define SBI_CONSOLE_GETCHAR 2
#define SBI_CLEAR_IPI 3
#define SBI_SEND_IPI 4
#define SBI_REMOTE_FENCE_I 5
#define SBI_REMOTE_SFENCE_VMA 6
#define SBI_REMOTE_SFENCE_VMA_ASID 7
#define SBI_SHUTDOWN 8

#define SBI_ECALL(__num, __a0, __a1, __a2)                                     \
  ({                                                                           \
    register unsigned long a0 asm("a0") = (unsigned long)(__a0);               \
    register unsigned long a1 asm("a1") = (unsigned long)(__a1);               \
    register unsigned long a2 asm("a2") = (unsigned long)(__a2);               \
    register unsigned long a7 asm("a7") = (unsigned long)(__num);              \
    asm volatile("ecall" : "+r"(a0) : "r"(a1), "r"(a2), "r"(a7) : "memory");   \
    a0;                                                                        \
  })

#define SBI_ECALL_0(__num) SBI_ECALL(__num, 0, 0, 0)
#define SBI_ECALL_1(__num, __a0) SBI_ECALL(__num, __a0, 0, 0)
#define SBI_ECALL_2(__num, __a0, __a1) SBI_ECALL(__num, __a0, __a1, 0)

#define SBI_PUTCHAR(__a0) SBI_ECALL_1(SBI_CONSOLE_PUTCHAR, __a0)
#define SBI_TIMER(__a0) SBI_ECALL_1(SBI_SET_TIMER, __a0)

struct sbiret {
  long error;
  long value;
};

#define SBI_EXTCALL(__EID, __FID, __a0, __a1, __a2)                            \
  ({                                                                           \
    register unsigned long a0 asm("a0") = (unsigned long)(__a0);               \
    register unsigned long a1 asm("a1") = (unsigned long)(__a1);               \
    register unsigned long a2 asm("a2") = (unsigned long)(__a2);               \
    register unsigned long a7 asm("a7") = (unsigned long)(__EID);              \
    register unsigned long a6 asm("a6") = (unsigned long)(__FID);              \
    asm volatile("ecall"                                                       \
                 : "+r"(a0), "+r"(a1)                                          \
                 : "r"(a2), "r"(a6), "r"(a7)                                   \
                 : "memory");                                                  \
  })

enum EID {
  SUSP = 0x53555350, // System Suspend
  HSM = 0x48534D,    // Hart State Management
  DBCN = 0x4442434E, // Debug Console
  sPI = 0x735049,    // s-mode IPI
};

struct sbiret sbi_extcall(enum EID EID, unsigned long FID, unsigned long a0,
                          unsigned long a1, unsigned long a2);
#endif
